#include <bits/stdc++.h>
using namespace std;

void solve() {
  int n, m;
  cin >> n >> m;
  vector<int> x(n), y(m);

  // 读取行最大值序列X和列最大值序列Y
  for (int i = 0; i < n; i++)
    cin >> x[i];
  for (int i = 0; i < m; i++)
    cin >> y[i];

  // 初始化结果矩阵，所有元素初始为0
  vector<vector<int>> ans(n, vector<int>(m, 0));

  // 创建包含1到N×M所有数字的集合s
  set<int> s;
  for (int i = 1; i <= n * m; i++)
    s.insert(i);

  // h数组用于统计每个数字在X和Y中出现的次数
  vector<int> h(n * m + 1, 0);
  map<int, int> mp; // 用于检查重复元素的辅助map
  int ok = 1;       // 标记是否能够构造成功

  // ========== 第一阶段：可行性检查 ==========

  // 检查X序列的合法性
  for (auto a : x) {
    h[a]++;          // 统计数字a出现的次数
    s.erase(a);      // 从剩余数字集合中移除a
    if (mp.count(a)) // 如果a已经在X中出现过，说明有重复
      ok = 0;
    mp[a] = 1; // 标记a已经出现过
  }
  mp.clear(); // 清空map，准备检查Y序列

  // 检查Y序列的合法性
  for (auto a : y) {
    h[a]++;          // 统计数字a出现的次数
    s.erase(a);      // 从剩余数字集合中移除a
    if (mp.count(a)) // 如果a已经在Y中出现过，说明有重复
      ok = 0;
    mp[a] = 1; // 标记a已经出现过
  }

  // 检查每个数字出现次数不超过2
  // 因为一个数字最多同时是行最大值和列最大值，所以最多出现2次
  for (auto a : h)
    if (a > 2)
      ok = 0;

  // ========== 第二阶段：贪心填充矩阵 ==========

  // 遍历矩阵的每个位置(i,j)
  for (int i = 0; i < n; i++) {
    for (int j = 0; j < m; j++) {
      // d取x[i]和y[j]中的较小值
      // 这是关键：我们希望尽可能使用能同时满足行列约束的数字
      int d = min(x[i], y[j]);

      // 策略1：优先使用能同时满足行列约束的数字
      // 条件1：h[d] == 1 表示d只在一个序列中出现过，可以安全使用
      // 条件2：x[i] == y[j] 表示这个位置正好是行列最大值的交点
      if (h[d] == 1 || x[i] == y[j]) {
        ans[i][j] = d; // 使用数字d填充当前位置
        h[d] = 0;      // 标记d已经被使用
        continue;      // 跳过后续策略
      }

      // 策略2：如果策略1不可用，从剩余数字中找最大的满足条件的数字
      // 使用upper_bound找到第一个大于d的数字
      auto it = s.upper_bound(min(x[i], y[j]));

      // 如果it指向集合开头，说明没有小于等于d的数字可用
      if (it == s.begin()) {
        ok = 0; // 构造失败
        break;  // 跳出内层循环
      }

      // 取前一个数字（即最大的小于等于d的数字）
      // 贪心思想：使用尽可能大的数字，给其他位置留更多选择
      ans[i][j] = *prev(it);
      s.erase(prev(it)); // 从集合中移除已使用的数字
    }
    if (!ok)
      break; // 如果已经失败，跳出外层循环
  }

  // ========== 第三阶段：输出结果 ==========

  if (ok) {
    cout << "Yes" << endl;
    // 输出构造成功的矩阵
    for (int i = 0; i < n; i++) {
      for (int j = 0; j < m; j++) {
        cout << ans[i][j] << " ";
      }
      cout << endl;
    }
  } else {
    cout << "No" << endl; // 输出构造失败
  }
}

int main() {
  int T;
  cin >> T;     // 读取测试用例数量
  while (T--) { // 处理每个测试用例
    solve();
  }
  return 0;
}